package com.onlyMe.net.library
{
	import com.flesler.componentas.core.Destroyable;
	import com.qb9.flashlib.lang.foreach;
	
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;
	
	public class Libraries implements Destroyable
	{
		private var packs:Object = {};
		private var loading:Object = {};
		private var queues:Object = {};
		
		private var root:String;
		private var shared:Boolean;
		
		// If weakKeys is true the libraries get auto-disposed!
		private var names:Dictionary = new Dictionary();
		
		public function Libraries(root:String='.', shared:Boolean=false)
		{
			if (root && root.slice(-1) !== '/')
				root += '/';
				
			this.root = root;
			this.shared = shared;
		}
		
		private function load(name:String):void
		{
			var lib:Library = new Library(shared, makeURL(name));
			lib.addEventListener(LibraryEvent.LOADED, libraryReady);
			lib.addEventListener(LibraryEvent.LOAD_FAILED, libraryReady);
			names[lib] = name;
		}
		
		private function makeURL(name:String):String
		{
			return root + name.split('.').join('/') + '.swf';
		}

		private function libraryReady(e:LibraryEvent):void
		{
			var lib:Library = e.target as Library;
			lib.removeEventListener(LibraryEvent.LOADED, libraryReady);
			lib.removeEventListener(LibraryEvent.LOAD_FAILED, libraryReady);
			loaded(names[lib], lib);
		}
		
		public function preload(pack:String):void
		{
			if (!loading[pack] && !packs[pack])
			{
				loading[pack] = true;
				load(pack);
			}
		}
			
		public function fetch(name:String, callback:Function, data:Object=null):void
		{
			var parts:Array = name.split('.');
			fetchFromPack(parts[0], parts[1], callback, data);
		}
		
		public function fetchFromPack(pack:String, name:String, callback:Function, data:Object=null):void
		{
			var item:Item = new Item(callback, pack, name, data);
			
			preload(pack);
			
			if (!loading[pack])
				setTimeout(executeItem, 1, item);
			else if (pack in queues)
				queues[pack].push(item);
			else
				queues[pack] = [item];
			
		}
		
		private function loaded(name:String, pack:Object):void
		{
			packs[name] = pack;
			
			foreach(queues[name], executeItem);
				
			delete queues[name];
			delete loading[name];
		}
		
		private function executeItem(item:Item):void
		{
			execute(item.callback, packs[item.pack], item.name, item.data);
		}
		
		private function execute(callback:Function, lib:Library, name:String=null, data:Object=null):void
		{
			var obj:Object = lib.loadFailed ? null : lib.getItem(name);
			if (data === null)
				callback(obj);
			else
				callback(obj, data);
		}
		
		private function destroyList(list:Object):void
		{
			for each (var item:Object in list)
				if (item is Destroyable)
					Destroyable(item).destroy();
		}
		
		public function get isDestroyed():Boolean
		{
			return packs === null;
		}
		
		public function destroy():void
		{
			destroyList(packs);
			destroyList(queues);
			
			queues = loading = packs = null;
		}
	}
}

	import com.flesler.componentas.core.Destroyable;

internal final class Item implements Destroyable
{
	internal var callback:Function;
	internal var pack:String;
	internal var name:String;
	internal var data:Object;
	
	public function Item(callback:Function, pack:String, name:String, data:Object)
	{
		this.callback = callback;
		this.pack = pack;
		this.name = name;
		this.data = data;
	}
	
	public function get isDestroyed():Boolean
	{
		return callback === null;
	}
	
	public function destroy():void
	{
		callback = null;
		data = null;
	}
}
